package com.share.tool.generator.api;

import com.alibaba.fastjson.JSON;
import com.share.common.enums.MethodMenu;
import com.share.common.utils.ClassUtil;
import com.share.tool.generator.api.config.BaseInfo;
import com.share.tool.generator.api.config.DirClassLoader;
import com.share.tool.generator.api.config.MyClassLoader;
import com.share.tool.generator.api.fm.FmHtmlFormat;
import com.share.tool.generator.api.model.CtrlInfo;
import com.share.tool.generator.api.model.MethodInfo;
import com.share.tool.generator.api.model.ParamInfo;
import com.share.tool.generator.api.postman.PostManFormat;
import com.share.tool.generator.api.vm.VelocityHtmlFormat;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * 根据java源代码文件，生产postman,fm,vm等格式的接口文档
 */
@Slf4j
public class MainClass2MoreApi {
    private static List<CtrlInfo> result = new LinkedList<>();
    private static BaseInfo baseInfo = new BaseInfo();

    public static void main(String[] args) throws Exception {
        DirClassLoader.loadJar("D:\\mavenRepository\\com\\sf");
        /*把class路径下的格式转为标准文件路径格式*/
//        final String classPath = new File(ClassLoader.class.getResource("/").getPath()).getPath();
        final String classPath = "D:\\project\\eos-svp-core-ms-jdk-zytr\\svp-its-client\\target\\classes";

        List<File> files = filterClassFile(new File(classPath), "Controller.,Service.,Manager.");
        ClassLoader loader = new MyClassLoader(classPath);
        for (File file : files) {
            //原文件名
            String filePath = file.getAbsolutePath();

            // 全类名
            String className = filePath.substring(classPath.length() + 1, filePath.length() - 6).replace(File.separatorChar, '.');

            // 设置类加载器
            Class<?> aClass = loader.loadClass(className);
            System.out.println("即将解析:" + aClass);

            CtrlInfo info = analysis(aClass);

            if (info != null) {
                result.add(info);
            }
        }
        System.out.println("类文件解析完毕");
        System.out.println(JSON.toJSONString(result));

        if (result != null && result.size() > 0) {
            String filePath = null;

            System.out.println("开始转换为postmain结构");
            filePath = PostManFormat.convertAndToFile(baseInfo, result);
            System.out.println("生产postMain-json文件:" + filePath);

            System.out.println("Fm开始转换为html结构");
            filePath = FmHtmlFormat.convert(baseInfo, result);
            System.out.println("生产的html文件:" + filePath);

            System.out.println("Vm开始转换为html结构");
            filePath = VelocityHtmlFormat.convert(baseInfo, result);
            System.out.println("生产的html文件:" + filePath);
        }

    }

    private static CtrlInfo analysis(Class<?> aClass) {
        String model = null;

        /*1.参数校验*/
        Controller controller = aClass.getAnnotation(Controller.class);
        RestController restController = aClass.getAnnotation(RestController.class);
        if (controller != null || restController != null) {
            model = "controller";
        }

        Consumes consumes = aClass.getAnnotation(Consumes.class);
        if (consumes != null) {
            model = "dubbo";
        }

        /*类型不适配直接退出*/
        if (model == null) {
            return null;
        }

        CtrlInfo info = new CtrlInfo();

        /*2.类路径*/
        if ("dubbo".equals(model)) {
            Path path = aClass.getAnnotation(Path.class);
            if (path != null) {
                String[] arr = delBeginOrEndChar("/", path.value());
                if (arr != null || arr.length > 0) {
                    info.setMapping(arr);
                }
            }
        } else {
            RequestMapping ctrlMapping = aClass.getAnnotation(RequestMapping.class);
            if (ctrlMapping != null) {
                String[] arr = delBeginOrEndChar("/", ctrlMapping.value());
                if (arr != null || arr.length > 0) {
                    info.setMapping(arr);
                }
            }
        }

        /*类注释*/
        Api api = aClass.getAnnotation(Api.class);
        if (api != null) {
            String value = api.value();
            info.setDescrip(value);
        } else {
            info.setDescrip(aClass.getName());/*没有备注的使用类名字*/
        }

        List methodList = new LinkedList();
        info.setMethodList(methodList);

        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            MethodInfo methodInfo = new MethodInfo();

            //方法请求地址
            if ("dubbo".equals(model)) {
                Path methodMapping = method.getAnnotation(Path.class);
                if (methodMapping != null) {
                    String[] arr = delBeginOrEndChar("/", methodMapping.value());
                    if (arr != null || arr.length > 0) {
                        methodInfo.setMapping(arr);
                    }
                }

                POST post = method.getAnnotation(POST.class);
                if (post != null) {
                    methodInfo.setMethod(MethodMenu.POST.code);
                }
                GET get = method.getAnnotation(GET.class);
                if (get != null) {
                    methodInfo.setMethod(MethodMenu.GET.code);
                }

            } else {
                RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                if (methodMapping != null) {
                    String[] arr = delBeginOrEndChar("/", methodMapping.value());
                    if (arr != null || arr.length > 0) {
                        methodInfo.setMapping(arr);
                    }

                    RequestMethod[] requestMethod = methodMapping.method();
                    if (requestMethod != null && requestMethod.length > 0) {
                        methodInfo.setMethod(requestMethod[0].name());
                    } else {
                        methodInfo.setMethod(MethodMenu.POST.code);/*默认生成post请求*/
                    }

                }

                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                if (postMapping != null) {
                    String[] arr = delBeginOrEndChar("/", postMapping.value());
                    if (arr != null || arr.length > 0) {
                        methodInfo.setMapping(arr);
                    }

                    methodInfo.setMethod(MethodMenu.GET.code);
                }

                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                if (getMapping != null) {
                    String[] arr = delBeginOrEndChar("/", getMapping.value());
                    if (arr != null || arr.length > 0) {
                        methodInfo.setMapping(arr);
                    }

                    methodInfo.setMethod(MethodMenu.GET.code);
                }

                /*方法上不写路径的直接跳过*/
                if (methodInfo.getMapping() == null || methodInfo.getMapping().length <= 0) {
                    continue;
                }
            }

            //方法注释
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            if (apiOperation != null) {
                String value = apiOperation.value();
                methodInfo.setDescrip(value);
            } else {
                methodInfo.setDescrip(method.getName());/*方法无备注使用方法名*/
            }

            //方法返回值---方便查看数据结构
            Class<?> returnType = method.getReturnType();
            if (returnType != null) {
                List<Object> responseList = new LinkedList<>();
                methodInfo.setResponse(responseList);
                List<String> classNameList = ClassUtil.listAllReturnTypeClass(method);
                if (classNameList != null && !classNameList.isEmpty()) {
                    int paramsCount = classNameList.size();
                    for (int i = 0; i < paramsCount; i++) {/*从第二个参数开始*/

                        String str = classNameList.get(i);
                        /*有的泛型返回的是个？号*/
                        if ("long,int,dubbo,float,boolean,".contains(str) || str.startsWith("java.lang") || str.startsWith("java.util")) {
                            if (i == 0) {
                                /*最外层的是基础数据类型*/
                                List<Object> tmpList = new LinkedList<>();
                                ParamInfo paramInfo = new ParamInfo();
                                paramInfo.setType(str);
                                tmpList.add(paramInfo);
                                responseList.add(tmpList);
                            } else {
                                continue;
                            }
                        }

                        /* 普通对象正常获取参数*/
                        try {
                            Class<?> aClass1 = Class.forName(str);
                            List<ParamInfo> classFiledAndbefd = listRemarksFromClass(aClass1, i == 0);
                            if (classFiledAndbefd != null) {
                                responseList.add(classFiledAndbefd);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

            /*参数绑定与解析*/
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes == null || parameterTypes.length == 0) {
                //方法无参数
            } else {
                int i = 0;
                for (Class parameterClass : parameterTypes) {
                    List<ParamInfo> fieldList = new LinkedList();
                    methodInfo.setParamList(fieldList);
                    if (parameterClass.getName().startsWith("java.util.") || parameterClass.getName().startsWith("javax.servlet.")) {
                        /*直接跳过*/
                    } else if (parameterClass.getName().startsWith("java.lang.")
                            || parameterClass.equals(MultipartFile.class)  /*文件上传*/
                            || "long,int,dubbo,float,boolean,".contains(parameterClass.getName() + ",")) {
                        ParamInfo paramInfo = new ParamInfo();

                        String[] varNames = ClassUtil.getMethodParameterNames(method);
                        if (varNames != null && varNames.length > i) {
                            paramInfo.setKey(varNames[i]);/*这里无法获取原始的方法名字,f*/
                        }
                        paramInfo.setType(parameterClass.getName());
                        paramInfo.setDescrip("基础数据类型");
                        fieldList.add(paramInfo);
                    } else {
                        List<ParamInfo> classFiledAndbefd = listRemarksFromClass(parameterClass, false);
                        if (classFiledAndbefd != null) {
                            fieldList.addAll(classFiledAndbefd);
                        }
                    }
                    i++;
                }
            }
            methodList.add(methodInfo);
        }

        return info;
    }

    /*获取属性名的解释*/
    public static List<ParamInfo> listRemarksFromClass(Class clazz, boolean allFieldFlag) {
        List<ParamInfo> fieldList = new LinkedList();
        Field[] fields = clazz.getDeclaredFields();
        Annotation apiModel = clazz.getAnnotation(ApiModel.class);
        //List<Field> fields = ClassUtil.getAllDeclaredFields(parameterClass);
        for (Field field : fields) {
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            if (apiModelProperty != null || apiModel != null || allFieldFlag) {
                ParamInfo paramInfo = new ParamInfo();
                paramInfo.setKey(field.getName());
                paramInfo.setType(field.getType().getName());
                if (apiModelProperty != null) {
                    paramInfo.setDescrip(apiModelProperty.value());
                }
                fieldList.add(paramInfo);
            }
        }
        return fieldList;
    }


    public static List<File> filterClassFile(File pFile, String fileNamefilter) {
        if (pFile == null || !pFile.exists()) {
            System.err.println("文件不存在:" + pFile.getAbsolutePath());
            return new LinkedList<>();
        }

        LinkedList<File> result = new LinkedList<>();

        File[] files = pFile.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                result.addAll(filterClassFile(file, fileNamefilter));
            } else {
                if (!file.getName().endsWith(".class")) {/*不是class文件直接跳过*/
                    continue;
                }

                if (fileNamefilter == null) {
                    result.add(file);
                } else {
                    String[] split = fileNamefilter.split(",");
                    for (String keyWord : split) {
                        if (file.getName().contains(keyWord)) {
                            result.add(file);
                            continue;
                        }
                    }
                }
            }
        }
        return result;
    }

    /*去除斜线*/
    public static String[] delBeginOrEndChar(String delTarget, String... data) {
        if (data == null || data.length <= 0) {
            return null;
        }
        if (delTarget == null) {
            return data;
        }

        List<String> list = new LinkedList<>();
        for (String str : data) {
            StringBuffer sb = new StringBuffer(str);
            while (sb.toString().startsWith(delTarget) && sb.length() > 0) {
                sb.replace(0, delTarget.length(), "");
            }

            while (sb.toString().endsWith(delTarget) && sb.length() > 0) {
                sb.replace(0, delTarget.length(), "");
                sb.substring(0, sb.length() - delTarget.length());
            }

            if (sb.toString().trim().length() > 0) {
                list.add(sb.toString());
            }
        }

        if (list.isEmpty()) {
            return null;
        } else {
            return list.toArray(new String[list.size()]);
        }
    }
}
